-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ZCPU Automated Testing console command & framework #18
Conversation
24e0f0a
to
ccd3087
Compare
61c10a9
to
2338ac2
Compare
ea967cf
to
779ea6e
Compare
Don't squash, it'll be squashed when merged |
Oh, did you manage to get an auto-squash in? If so, cool! |
Nah it's just that default wasn't set to squash and I guess sparky forgot to squash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skimmed very quickly. Need to look into it further and actually test it later on. Just found this. Initial thoughts:
-
Shouldn't print successful tests, just print Passed/Failed. E2 test suite is currently at 100 tests (and those consist of smaller tests already). Would spam console a lot as the test suite inevitably got larger.
-
On the same note, would be nice if you could suppress that hl-zasm output print
-
I think testing at the entity level is not necessary might be a nightmare to manage. Couldn't you just have an instance manually created and track the addresses it writes to instead?
I can do 1 and 2 easily(sometime in the morning), but I'm a bit confused on what you mean by "testing at the entity level" |
Might've misread the code, I just saw the |
Well, it was copied from the entity implementation, but it seems like I forgot to comment one out, I'll get to it in the morning |
3080065
to
299bc81
Compare
@Vurv78 Does the code here meet your expectations or is there anything else you think I should work on before it's merge-worthy? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want zcpu on workshop, probably want to move the tests directory to the top level, so it doesn't bog down the download.
That or include it in the ignored files in addon.json
lua/wire/zvm/zvm_tests.lua
Outdated
ZVMTestSuite.Warnings = 0 | ||
ZVMTestSuite.TestFiles = {} | ||
for filename in string.gmatch(names, '[^,]+') do | ||
local files = file.Find('lua/'..testDirectory..'/'..filename..'.lua',"GAME") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would there ever be multiple files returned here? Simplify this to just file.Read
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I split on commas, so you can type test1,test2
to run test1.lua and test2.lua
file.Find seems to allow * which I don't strip from the string, so you can type *example*
to run every test with the word example
in it (file_example.lua, example.lua)
So you can debug with a few tests at a time rather than all of them at once if you specify the names in a comma delimited list.
lua/wire/zvm/zvm_tests.lua
Outdated
finalFail = fail | ||
end | ||
if ZVMTestSuite.CurrentWarnings > 0 then | ||
print("Compiler Warnings from "..ZVMTestSuite.TestQueue[#ZVMTestSuite.TestQueue]..": "..ZVMTestSuite.CurrentWarnings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think showing warnings is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intent was that they can be used to try and catch regressions in compilation & syntax, if we checked a prior commit for how many warns were produced vs now we can try to track down code changes that cause more warnings to determine if the warnings are intentional or not.
Though this will only display the NUMBER of warnings the test had, and not any of the actual warning text itself for the moment.
Right now all the warnings that are in during the state of this PR include:
"Trying to generate invalid code"
"Instruction "(opcode name)" is obsolete/an old mnemonic"
"Using complex expression as operand: might corrupt user register"
"Warning: using an unpreserved register"
"Defining a vector inside a function block might cause issues"
Which, I agree that a lot of these aren't exactly necessary to test for, but "Trying to generate invalid code" indicates labels and registers not being used in expressions, which could occur if a user expression exits before all of the values are used, ZAP R0 R1
not parsing R1 for example should generate this warning.
I know I'm very late to review this, but I don't like how much this test framework uses globals and relies on you creating and using a lot of functions you could easily mess up. I would just want to keep it as simple as possible, zero globals used here: -- Either have a comment annotation here or have it as a field in the returned table for whether the test *should* fail
local Test = {}
function Test.Setup()
-- some initial stuff passed here, optional function
end
function Test.Teardown()
-- also optional
end
function Test.Run(cpu, pass, fail)
-- do asynchronous stuff and run pass() fail() function callbacks given
end
return Test If you're going to be the only one working on it, then it's fine as long as you can work with it, but for me, this is just way too complex: I'd probably have a more fleshed out example if I had more knowledge on the cpu. This is a test suite after all and users wouldn't have to interact with it, but if it isn't easy to add tests then it might discourage future devs to create proper tests |
I'm cool with just putting it in the ignored of the addon.json for now, if you know how to include it from cpu_load.lua in the autorun folder or know another way to force it to be included for those who have it when it's in the base dir let me know and I can do that instead. |
The test framework can, and probably should go through some revision. The initial structure this took was because we needed to wait for compile to complete since it was async(ran on a timer), since the compile is now synchronous(the one exposed by ZVMTestSuite) I could simplify it to just return the compiled code(a table of indexed numbers), so we can do away with the RunTest/RunCPU crap.
I'll probably make intentional fail tracked in the returned local test's table, so we can just check if it "ExpectsFail" and track those as a separate number from the passed tests when counting it on fail.
If this is intended to be a "cleanup" function, I actually can't imagine what we'd need to cleanup, right now we're creating a fresh VM for every test, not passing the same one around. I could make one that calls(if it exists) after pass or fail(just "if finished" basically) and call it something like "Test.OnFinish(bool fail)", if that's your intent.
I'd prefer it to have the ability to be powerful without having to reinvent the wheel for most cases, but still simple enough for future devs to pick up and understand without having to look into the internals. I'd initially thought it was intuitive but if you say otherwise I'll try and work with any suggestions you have for correcting it. Here's an example of a potential revised test, let me know what you think of it, once we've got the "revised" test to an ideal state I can get to work on implementation and updating the tests. -- a potential revision using example.lua as a base.
local Test = {}
function Test.Setup(CPU, TestSuite)
end
function Test.OnFinish(fail, CPU, TestSuite)
end
function Test.CompileError(msg,failCB)
end
-- ^ This one isn't used by the framework, it's a user func that's passed to
-- TestSuite:Deploy to call on a fail right now, but we could switch the func to return bool
-- on pass or fail instead to allow user to do
-- if TestSuite:Deploy() then
-- work
-- else
-- fail
-- end
-- Whichever way is deemed more intuitive, whether true on success or true on error
function Test.Run(CPU, TestSuite, passCB, failCB)
-- Simpler function to compile and upload code to a device
-- Compile can still exist, if the test creator needs to read or modify the results.
TestSuite:Deploy(CPU,"x: INC R0 JMP x",Test.CompileError)
CPU.Clk = 1
for i = 0, 4096 do
CPU:RunStep()
end
if CPU.R0 == 4096 then
-- The CPU ran 4096 blocks that were just adding 1 to R0
-- so it should be 4096.
return passCB()
else
TestSuite.Error("R0 is not 4096! R0 is " .. tostring(CPUTest.VM.R0))
-- Can make the failCB take similar args to Error instead.
return failCB()
end
end
return CPUTest |
No, I just wanted the simplest api possible, if it's not necessary, great, no need to implement it
Since you said something like Teardown isn't needed, can remove that part. Third option could be |
Tests can have a virtual filesystem(still falls back to actually using the folder if you don't use it) Easier to use "Deploy" function, for compiling and flashing straight to a device Revises all tests to use assert inside of a pcalled context
@Vurv78 Got them all to compile and pass with your suggestion. With assert, we even get the file & line it occurred on, which makes it easier to track down programming errors |
@Vurv78 Just wanted to follow up, was there any further modification necessary to the framework before this has your approval? Maybe merge master back into this and update the addon.json to ignore the tests folder? |
Creates basic framework & concmd that runs an entire list of lua files from a folder (lua/wire/zvm/tests), has options for creating & simulating a virtual IOBus device and/or a simple virtual readable/writable membus, plus
57910 example tests at the momentVideo of command output
(only live updates like this if pause is disabled, like in mp)
16.compiles.10.tests.mp4
Command output and usage:
See wire/zvm/tests/example.lua for a simple example of constructing your own tests (Increments R0 in 4096 steps and checks if it's equal to 4096, else it will fail)
This should be expanded later, more tests, smarter tests, I don't know how many of these test cases I can write because I might disappear soon, but hopefully this test framework is enough that the community can get in on it too